package cn.eng.basic.service.impl;

import cn.eng.basic.commonenum.ErrorCode;
import cn.eng.basic.constants.WxConstant;
import cn.eng.basic.domain.dto.LoginDto;
import cn.eng.basic.exception.BusinessException;
import cn.eng.basic.jwt.JwtUtils;
import cn.eng.basic.jwt.LoginData;
import cn.eng.basic.jwt.RsaUtils;
import cn.eng.basic.service.ILoginService;
import cn.eng.basic.utils.AjaxResult;
import cn.eng.basic.utils.HttpUtil;
import cn.eng.basic.utils.Md5Utils;
import cn.eng.basic.utils.StrUtils;
import cn.eng.org.mapper.EmployeeMapper;
import cn.eng.system.domain.Menu;
import cn.eng.user.domain.Logininfo;
import cn.eng.user.domain.User;
import cn.eng.user.domain.Wxuser;
import cn.eng.user.mapper.LogininfoMapper;
import cn.eng.user.mapper.UserMapper;
import cn.eng.user.mapper.WxuserMapper;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.security.PrivateKey;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Service
@SuppressWarnings("all") //抑制代码错误
public class LoginServiceImpl implements ILoginService {
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private LogininfoMapper logininfoMapper;
    @Autowired
    private WxuserMapper wxuserMapper;
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private EmployeeMapper employeeMapper;
    /**
     * 账户登录
     * @param loginDto
     * @return
     */
    @Override
    @Transactional
    public AjaxResult account(LoginDto loginDto) {
        //空置校验
        //判断用户是否存在
        Logininfo logininfo = logininfoMapper.loadByUsername(loginDto);
        if (logininfo == null){
            throw new BusinessException(ErrorCode.ACCOUNT_NULL.getCode());
        }
        //校验密码是否正确
        String passwordMd5 = Md5Utils.encrypByMd5(logininfo.getSalt() + loginDto.getPassword());

        if (!passwordMd5.equals(logininfo.getPassword())){
            throw new BusinessException(ErrorCode.PASSWORD_ERROR.getCode());
        }
        //判断该账号是否已禁用
        if (!logininfo.getDisable()){
            throw new BusinessException(ErrorCode.DISABLE_ERROR.getCode());
        }
        //将信息保存redis中
//        UUID token = UUID.randomUUID();
//        redisTemplate.opsForValue().set(token,logininfo,30, TimeUnit.MINUTES);
//        //返回token 和登录信息
//        logininfo.setSalt(null);
//        logininfo.setPassword(null);
//        Map<String,Object> map = new HashMap<>();
//        map.put("token",token);
//        map.put("logininfo",logininfo);
        Map<String, Object> resultMap = loginSuccessJwtHandler(logininfo);
        return AjaxResult.me().setObj(resultMap);
    }

    /**
     * 微信扫码登录
     * @param code
     * @return
     */
    @Override
    @Transactional
    public AjaxResult wechatLogin(String code) {
        //组装调用访问微信接口-url地址
        String url= WxConstant.GET_ACK_URL
                    .replace("APPID",WxConstant.APPID)
                    .replace("SECRET",WxConstant.SECRET)
                    .replace("CODE",code);
        //发送访问微信接口的请求
        String httpGet = HttpUtil.httpGet(url);
        System.out.println(httpGet);
        //把获取的字符串数据转为Json对象
        JSONObject jsonObject = JSONObject.parseObject(httpGet);
        //获取请求之后的返回数据
        String access_token = jsonObject.getString("access_token");//拿到token
        String openid = jsonObject.getString("openid");//拿到openid
        //根据openid 去数据库查询 是否存在此用户
        Wxuser wxuser = wxuserMapper.loadByOpenId(openid);
        //判断 openid是否为空 不为空 就免密登陆
        if (wxuser != null){
            //根据 wxuser 里面的user_id ，去查询user表
           User user =  userMapper.loadByUserId(wxuser.getUserId());
           if (user != null){//绑定过user
               //再根据user里面的logininfoId 去查询logininfo
               Logininfo logininfo = logininfoMapper.loadByUserLogininfoId(user.getLogininfoId());
               //免密登陆
               //将信息保存redis中
//               UUID token = UUID.randomUUID();
//               redisTemplate.opsForValue().set(token,logininfo,30, TimeUnit.MINUTES);
//               //返回token 和登录信息
//               logininfo.setSalt(null);
//               logininfo.setPassword(null);
//               Map<String,Object> map = new HashMap<>();
//               map.put("token",token);
//               map.put("logininfo",logininfo);
               Map<String, Object> resultMap = loginSuccessJwtHandler(logininfo);
               return AjaxResult.me().setObj(resultMap);
           }
        }
        //如果 没有查询到 openid  表示第一次登录或者未绑定，拼接access_token和openid返回给前端
        String params = "?access_token="+access_token+"&openid="+openid;
        return AjaxResult.me().setSuccess(false).setObj(params);
    }

    /**
     * 微信绑定
     * @param map
     * @return
     */
    @Override
    @Transactional
    public AjaxResult wechatBinder(Map<String, String> map) {
        //获取map里面的参数
        String phone = map.get("phone");
        String verifyCode = map.get("verifyCode");
        String accessToken = map.get("accessToken");
        String openid = map.get("openid");
        //判断参数
        if (StringUtils.isEmpty(phone)){//手机不能为空
            throw new BusinessException(ErrorCode.PHONE_NULL.getCode());
        }
        if (StringUtils.isEmpty(verifyCode)){//图形验证码不能为空
            throw new BusinessException(ErrorCode.IMGCODE_NULL.getCode());
        }
        //获取redis 里面的图形验证码
        Object binderCode = redisTemplate.opsForValue().get("binder:" + phone);
        if (binderCode == null){//为空 图形验证码过期
            throw new BusinessException(ErrorCode.IMGCODE_TIME_OUT.getCode());
        }
        //比对图形验证码是否正确
        if (!binderCode.toString().split(":")[0].equals(verifyCode)){
                throw new BusinessException(ErrorCode.IMGCODE_ERROR.getCode());
        }
        //组装第三次访问后端接口的url地址
        String url=WxConstant.GET_USER_URL
                    .replace("ACCESS_TOKEN",accessToken)
                    .replace("OPENID",openid);
        //发送器请求
        String jsonStr = HttpUtil.httpGet(url);
        //封装Wxuser 对象
        Wxuser wxuser = jsonStr2Wxuser(jsonStr);
        //判断wxuser 是否存在openid
        Wxuser byOpenId = wxuserMapper.loadByOpenId(openid);
        if (byOpenId != null){
            wxuserMapper.remove(byOpenId.getId());
        }
        //查询是否存在phone 数据 并拿到user对象
        User user = userMapper.loadByPhone(phone);
        if(user == null){
            //封装user对象
            user = phone2User(phone);
            //封装logininfo对象
            Logininfo logininfo = user2Logininfo(user);
            logininfoMapper.save(logininfo);
            user.setLogininfoId(logininfo.getId());
            userMapper.save(user);
        }
        //如果不为空，就把userID和wxuser进行绑定
        wxuser.setUserId(user.getId());
        //添加wxuser数据
        wxuserMapper.save(wxuser);
        //查询logininfo对象
        Logininfo logininfo = logininfoMapper.loadById(user.getLogininfoId());
       //免密登陆
//        UUID token = UUID.randomUUID();
//        redisTemplate.opsForValue().set(token,logininfo,30, TimeUnit.MINUTES);
//        //返回token 和登录信息
//        logininfo.setSalt(null);
//        logininfo.setPassword(null);
//        Map<String,Object> map1 = new HashMap<>();
//        map1.put("token",token);
//        map1.put("logininfo",logininfo);
        //调用loginSuccessJwtHandle方法
        Map<String, Object> resultMap = loginSuccessJwtHandler(logininfo);
        return AjaxResult.me().setObj(resultMap);
    }

    /**
     * 使用jwt技术进行加密
     * @param logininfo
     * @return
     */
    private Map<String,Object> loginSuccessJwtHandler(Logininfo logininfo){
        //jwt token => 私钥 + LoginData【logininfo + permissions + menus】
        //本来就只需要传递token到前端就可以了，因为token中数据全都有，只需要解密就可以了
        //前端ks代码没有写解密的代码。所以除了将token传递到前端，还需要传递
        //1.logininfo-显示用户信息 2.menus-动态路由-动态菜单 3.permissions-资源权限/按钮权限 4.token-后续发请求要判断是否登录了
        //前台用户没有权限和菜单，前台用户的Map中只有：登录信息logininfo，token
        Map<String,Object> map = new HashMap<>();
        LoginData loginData = new LoginData();
        map.put("logininfo",logininfo);
        loginData.setLogininfo(logininfo);
        if(logininfo.getType()==0){//0-表示管理员
            //一：想办法获取当前登录人的权限：permissions - 查那些字段
            List<String> permissions = employeeMapper.getPermissionsByLogininfoId(logininfo.getId());
            map.put("permissions",permissions);
            loginData.setPermissions(permissions);
            //二：想办法获取当前登录人的菜单：menus - 查那些字段
            List<Menu> menus = employeeMapper.getMenusByLogininfoId(logininfo.getId());
            map.put("menus",menus);
            loginData.setMenus(menus);
            //三：将permissions，menus，logininfo封装到LoginData中
        }
        try {
            //四：通过工具类获取私钥
            PrivateKey privateKey = RsaUtils.getPrivateKey(JwtUtils.class.getClassLoader().getResource("auth_rsa.pri").getFile());
            //五：使用工具类和私钥将LoginData加密 => token[jwt token]
            String jwtToken = JwtUtils.generateTokenExpireInMinutes(loginData, privateKey, 3000);
            //六：将permissions，menus，logininfo，token[jwt token]装到Map中响应给前端
            map.put("token",jwtToken);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return map;
    }


    /**封装logininfo数据**/
    private Logininfo user2Logininfo(User user) {
        Logininfo logininfo = new Logininfo();
        //copy 封装logininfo
        BeanUtils.copyProperties(user,logininfo);
        logininfo.setType(1);
        logininfo.setDisable(true);
        return logininfo;
    }

    /**封装user数据**/
    private User phone2User(String phone) {
        //创建user 对象
        User user = new User();
        //封装user数据
        user.setUsername(phone);
        user.setPhone(phone);
        //随机生成盐值
        String salt = StrUtils.getComplexRandomString(32);
        user.setSalt(salt);
        //获取随机密码
        String password = StrUtils.getRandomString(1);
        //加密加盐
        String md5Password = Md5Utils.encrypByMd5(salt + password);
        user.setPassword(md5Password);
        user.setState(1);
        user.setCreatetime(new Date());
        return user;
    }

    /**封装wxuser数据**/
    private Wxuser jsonStr2Wxuser(String jsonStr) {
        //把String 转化成 json数据
        JSONObject jsonObject = JSONObject.parseObject(jsonStr);
        //创建 wxuser 对象
        Wxuser wxuser = new Wxuser();
        //封装wxuser数据
        wxuser.setOpenid(jsonObject.getString("openid"));
        wxuser.setNickname(jsonObject.getString("nickname"));
        wxuser.setHeadimgurl(jsonObject.getString("headimgurl"));
        wxuser.setUnionid(jsonObject.getString("unionid"));
        return wxuser;
    }
}
